package Implementation;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import utilities.Amostragem;
import entility.Machine;
import entility.Range;

public class Intervalos {


	public List<UniqueInterval> intervals ;

	public Intervalos(Map<Machine, List<Range>> rangeMap) {

		this.intervals = new LinkedList<UniqueInterval>();

		for(Machine m: rangeMap.keySet())
		{

			for(Range r: rangeMap.get(m))
			{
				UniqueInterval uniqueInterval = new UniqueInterval(m, r);
				this.intervals.add(uniqueInterval);
			}

		}

	}


	public void uniqueInterval()
	{
		List<UniqueInterval> uniques = new LinkedList<UniqueInterval>();
		Queue<UniqueInterval> theUniques = new ConcurrentLinkedQueue<UniqueInterval>(intervals);

		while(theUniques.size() > 0)
		{
			UniqueInterval unique = theUniques.poll();
			if(unique.fit(theUniques))
				uniques.add(unique);
		}

		theUniques = new ConcurrentLinkedQueue<UniqueInterval>(intervals);
		theUniques.removeAll(uniques);

		while(theUniques.size() > 0)
		{
			UniqueInterval unique = theUniques.poll();
			UniqueInterval toAdd = unique.getHit(theUniques);
			if(toAdd!= null )
				uniques.add(toAdd);

		}

		this.intervals = uniques;

	}


	public void validade(long min, long max)
	{
		List<UniqueInterval> theUniques = new ArrayList<UniqueInterval>(intervals);

		List<UniqueInterval> uniques = new ArrayList<UniqueInterval>();

		Machine host = this.getMinHost(theUniques);
		int pos = 0;
		int r = 0;
		
		UniqueInterval minUnique = theUniques.get(0);
		theUniques.remove(0);
		
		for(UniqueInterval unique: theUniques )
		{
			if(unique.range.min < minUnique.range.min)
			{
				minUnique = unique.cloneUnique();
				r = pos;
			}
			pos++;

		}
		
		
		if(min < minUnique.range.min)
		{
			UniqueInterval interval = new UniqueInterval(host, new Range((int)min, minUnique.range.min));
			uniques.add(interval);
		}
		uniques.add(new UniqueInterval(minUnique.host,new Range( minUnique.range.min, minUnique.range.max)));
		UniqueInterval lastUnique = new UniqueInterval(minUnique.host,new Range( minUnique.range.min, minUnique.range.max));
		
		theUniques.remove(r);
		
		while (theUniques.size() > 0) {

			pos = 0;
			r = 0;
			minUnique.range.min = (int)max;
			for(UniqueInterval unique: theUniques )
			{
				if(unique.range.min < minUnique.range.min)
				{
					minUnique = unique.cloneUnique();
					r = pos;
					
				}
				pos++;

			}
			
			boolean add = true;
			if(minUnique.range.min - lastUnique.range.min < 1 ||minUnique.range.max - lastUnique.range.max < 1 )
			{
				
				if(minUnique.range.min == lastUnique.range.min && minUnique.range.max == lastUnique.range.max)
				{
					add= false;
					
				}
				else
				{
					int Gmin = Math.abs(minUnique.range.max - minUnique.range.min);
					int Glast = Math.abs(lastUnique.range.max -lastUnique.range.min);
					
					if(Gmin > Glast)
					{
						uniques.remove(uniques.size() -1);
						
					}
						
					else
						add = false;
										
				}
				
			}
			
			if(add)
			{
				
				if(lastUnique.range.max < minUnique.range.min)
					
					uniques.add(new UniqueInterval(minUnique.host, new Range(minUnique.range.min, minUnique.range.max) ) );
			}
			lastUnique = new UniqueInterval(minUnique.host, new Range(minUnique.range.min, minUnique.range.max) );
				
			theUniques.remove(r);

		}
		
		
		
		UniqueInterval lastInserted = uniques.get(uniques.size() - 1);
		if(lastInserted.range.max < max)
		{
			UniqueInterval interval = new UniqueInterval(host, new Range(lastInserted.range.max,(int) max));
			uniques.add(interval);
		}
		
		
		List<UniqueInterval> intervalosFinais = new ArrayList<UniqueInterval>();

		int minRange = uniques.get(0).range.min;
		UniqueInterval lastInsert= new UniqueInterval(host,new Range(minRange, minRange) );
		
		for(UniqueInterval unique: uniques)
		{
			if(Math.abs(unique.range.min - lastInsert.range.max) > 1)
			{
				intervalosFinais.add( new UniqueInterval(getMinHost(new ArrayList<UniqueInterval>(intervalosFinais)), new Range(lastInsert.range.max , unique.range.min )));
			}
			intervalosFinais.add(unique);
			
			lastInsert = new UniqueInterval(unique.host, new Range(unique.range.min, unique.range.max));
			
		}
		
		
		this.intervals.clear();
		System.gc();
		
		this.intervals = new ArrayList<UniqueInterval>(intervalosFinais);
		

	}

	private Machine getMinHost(List<UniqueInterval> intervalos) {

		Map<Machine, Integer> maps = new HashMap<Machine, Integer>(); 
		
		for(UniqueInterval unique:intervalos)
		{
			if(maps.containsKey(unique.host))
			{
				Integer number = maps.get(unique.host);
				number++;
				maps.put(unique.host, number);
				
			}
			else
			{
				maps.put(unique.host, 1);
			}
			
			
		}
		
		int min = 900000;
		Machine host = intervalos.get(0).host;
		
		for(Machine m: maps.keySet())
		{
			if( maps.get(m) < min )
			{
				host = m;
				min = maps.get(m);
				
			}
			
			
		}
		
		return host;


	}


	public Map<Machine, List<Range> > toMap()
	{
		
		Map<Machine, List<Range>> maps = new HashMap<Machine, List<Range> >();
		for( UniqueInterval unint: this.intervals)
		{
			
			if(maps.containsKey(unint.host))
			{
				List<Range> list = maps.get(unint.host);
				list.add(new Range(unint.range.min, unint.range.max));
				maps.put(unint.host, list);
				
			}
			else
			{
				List<Range> ranges = new ArrayList<Range>();
				
				ranges.add(unint.getRange());
				
				maps.put(unint.host, ranges);
				
				
			}
			
		}
		return maps;

	}


	public static void main(String args[])
	{

		Map<Machine, List<Range>> maps = new HashMap<Machine, List<Range>>();

		String machines[] = {"200.239.163.69:8080", "200.239.645.239:7000", "200.239.789.180:8080", "200.239.213.333:8080", "200.239.693.777:8080"};

		for(String s: machines)
		{

			Amostragem a = new Amostragem(new File("E:\\workspace\\TPSDInterfaces\\files"), 1000);
			Queue<Range> r = a.getBuckets(10);
			maps.put(new Machine(s),new ArrayList<Range>(r).subList(0, 5) );
		}

		Intervalos inter = new Intervalos(maps);

		inter.uniqueInterval();
		inter.validade(-1000, 1000);
		
		System.out.println(inter.toMap());
		
		


	}




}
